/*
 * CNetworkManager.cpp
 *
 *  Created on: 11.08.2011
 *      Author: kirill
 */

#include "CNetworkManager.h"


CNetworkManager::CNetworkManager():
	maxConnectionCount(0), allowConnections(true)
{

	incomeQueue = new TThreadSafeQueue<STANDART_NETWORK_MESSAGE*>( NETWORK_MAX_QUEUE_LENGHT );

	networkSpeaker = new CNetworkSpeaker( incomeQueue );
	memset( Connections, 0, sizeof(l_CONNECTION_DESCRIPTOR) * MAX_PLAYERS_COUNT );

	outMessagesArray = new std::vector<STANDART_NETWORK_MESSAGE*>();
	RandNumber++;
}

CNetworkManager::~CNetworkManager()
{
	ReadingThreadAllow = false;
	//threadProcessMessages->join(); // Waiting for thread finished
	delete outMessagesArray;

	delete networkSpeaker;

	delete incomeQueue;

	for ( UINT i = 0; i < MAX_PLAYERS_COUNT; i++ )
	{
		BreakConnection( i, false );
	}
	// TODO Добавить освобождение всех аллоцированных ресурсов
}

void
CNetworkManager::CreateConnection( const char *IpAddress, const UINT FirstPort, const UINT LastPort )
{
	PrepareConnection( 1 );
	boost::asio::ip::address_v4 IpAddressDigits = boost::asio::ip::address_v4::from_string( IpAddress );
    networkSpeaker->StartConnection( &IpAddressDigits, FirstPort, LastPort,  boost::bind( &CNetworkManager::ConnectionCreateCallback, this, _1, _2) );
    StopCreatingConnections(); //Not allow automatic recreate client connection on disconnect
    RunProcessMessages();
}

void
CNetworkManager::CreateConnection( const UINT FirstPort, const UINT LastPort, const UINT MaxConnectionCount )
{
	PrepareConnection( MaxConnectionCount );
	networkSpeaker->StartConnection( NULL, FirstPort, LastPort,  boost::bind( &CNetworkManager::ConnectionCreateCallback, this, _1, _2) );
	RunProcessMessages();
}

void
CNetworkManager::BreakConnection( const BYTE ConnectionId, const bool SendMessage )
{
	if ( Connections[ConnectionId].connected )
	{
		Connections[ConnectionId].connected = false;
		Connections[ConnectionId].connection->Disconnect( SendMessage );
		//boost::this_thread::sleep( boost::posix_time::milliseconds( 600 ) );
		//delete Connections[ConnectionId].connection;
		Connections[ConnectionId].checkConnection = false;

		if ( allowConnections )
		{
			networkSpeaker->GoOnConnecting();// Allow new connections
		}
	}
}

void
CNetworkManager::StopCreatingConnections()
{
	allowConnections = false;
}

void
CNetworkManager::SendMessages( const std::vector<STANDART_NETWORK_MESSAGE*> &Messages )
{
	fprintf( stderr, "[MANAGER] SendMessages - %u \n", Messages.size() );
	for ( UINT i = 0; i < Messages.size(); i++ )
	{
		UINT tmpId = Messages[i]->header.clientId;
		Connections[tmpId].connection->SendMessage( Messages[i] );// TODO Выкидываем сообщение об ошибке если вернули false
	}
}

std::vector<STANDART_NETWORK_MESSAGE*>*
CNetworkManager::GetMessages()
{
	std::vector<STANDART_NETWORK_MESSAGE*>* tmpPointer = outMessagesArray;
	outMessagesArray = new std::vector<STANDART_NETWORK_MESSAGE*>();
	return tmpPointer;
}

BYTE
CNetworkManager::ConnectionCreateCallback( IConnection* Connection, const UINT ConnectionID )
{
	fprintf( stderr, "[MANAGER] ConnectionCreateCallback - %u\n", ConnectionID );
	BYTE Result = 0;
	if ( ConnectionID < maxConnectionCount )
	{
		if ( Connections[ ConnectionID ].connected != false )
		{
			delete Connections[ ConnectionID ].connection;
		}

		Connections[ ConnectionID ].connected = true;// TODO Рефакторинг бля... Конструктор для структуры
		Connections[ ConnectionID ].checkConnection = false;
		Connections[ ConnectionID ].connection = Connection;
		Result |= CREATE_CONNECTION_RESULT_OK;

		ConnectionNotify( ConnectionID, true ); //Notify system about new connection

		if ( ConnectionID < (maxConnectionCount - 1) ) // Is it free slots?
		{
			Result |= CREATE_CONNECTION_RESULT_NEXT;
		}
	}
	fprintf( stderr, "[MANAGER] ConnectionCreateCallback - Result 0x%X - %u \n", Result, ConnectionID );
	return Result;
}

void
CNetworkManager::TimerHandler( UINT TimerId )
{
	// TODO Добавить проверку на превышение тайм-аута ожидания (Connections[].checkConnection)
}

void
CNetworkManager::PrepareConnection( UINT MaxConnectionCount )
{
	for ( UINT i = 0; i < maxConnectionCount; i++ )
	{
		if ( Connections[i].connection != NULL )
			delete Connections[i].connection;
	}
	memset( Connections, 0, sizeof(l_CONNECTION_DESCRIPTOR) * MAX_PLAYERS_COUNT );

	maxConnectionCount = MaxConnectionCount;//Only one connection allowed for client
	allowConnections = true;
}

void
CNetworkManager::RunProcessMessages()
{
	ReadingThreadAllow = true;
	if ( outMessagesArray != NULL )
	{
		int ArraySize = outMessagesArray->size();
		for ( int i = 0; i < ArraySize; i++ )
		{
			delete (*outMessagesArray)[i];
		}
		outMessagesArray->clear();
	}
	else
	{
		outMessagesArray = new std::vector<STANDART_NETWORK_MESSAGE*>();
	}

	boost::thread threadProcessMessages = boost::thread( boost::bind( &CNetworkManager::ProcessMessagesThread, this ) );
}

void
CNetworkManager::ProcessMessagesThread()
{
    while ( ReadingThreadAllow )
    {
    	STANDART_NETWORK_MESSAGE* tmpMessage = NULL;
    	if ( incomeQueue->Receive( &tmpMessage ) )
    	{
    		fprintf( stderr, "[MANAGER] ProcessMessagesThread() - tmpMessage %p \n", tmpMessage );
    		NETWORK_MESSAGE_HEADER Header = tmpMessage->header;
    		BYTE CurrentClientId = Header.clientId;

    		if ( NETWORK_PROTOCOL_VERSION == Header.version ) // Check connection
    		{
    			Connections[ CurrentClientId ].checkConnection = true;
    		}
    		else
    		{
    			fprintf( stderr, "[CRASH] ProcessMessagesThread() Incompatable protocol version\n");
    			return; // TODO Полная хана
    		}

    		if ( NET_MESSAGE_GROUP_ACTION == Header.group ) // On network level only some actions process
    		{
				switch ( Header.value )
				{
				break; case NET_MESSAGE_ACTION_GREETING:
					{
						if ( NET_MESSAGE_ADD_INITIAL_GREETING == Header.additionalData1 ) // Successful connected
						{
							//Connections[ tmpMessage->header.clienID ].checkConnection = true;
						}
					}
				break; case NET_MESSAGE_ACTION_REQUEST:// Test connection
					{
						if ( NET_MESSAGE_ADD_CHECK_CONNECTION == Header.additionalData1 )
						{
							ConfirmConnection( Header.clientId );
						}
					}
				break; default:
					{

					}
				}
    		}

    		if ( NULL != tmpMessage )
    		{
    			fprintf( stderr, "[MANAGER] Push back message \n");
    			outMessagesArray->push_back( tmpMessage );
    		}
    	}
    	else
    	{
    		boost::this_thread::sleep( boost::posix_time::milliseconds( DEFAULT_NETWORK_WAIT_FOR * 2 ) );
    	}

    }
}

void
CNetworkManager::ConnectionNotify( const BYTE ConnectionId, const bool Estabilished )
{
	STANDART_NETWORK_MESSAGE* NotifyMessage = new
			                  STANDART_NETWORK_MESSAGE( NETWORK_PROTOCOL_VERSION, NET_MESSAGE_GROUP_ACTION,
														Estabilished ? NET_MESSAGE_ACTION_ESTABILISHED : NET_MESSAGE_ACTION_REJECTION,
														ConnectionId );
	if ( !incomeQueue->Send( NotifyMessage ) )
	{
		return;  // TODO Полная хана
	}
}

void
CNetworkManager::ConfirmConnection( const BYTE ConnectionId )
{
	STANDART_NETWORK_MESSAGE* NotifyMessage = new
				                  STANDART_NETWORK_MESSAGE( NETWORK_PROTOCOL_VERSION, NET_MESSAGE_GROUP_ACTION,
				                		                    NET_MESSAGE_ACTION_REQUEST,
															ConnectionId );
	NotifyMessage->header.additionalData1 = NET_MESSAGE_ADD_CONFIRM_CONNECTION;
	Connections[ ConnectionId ].connection->SendMessage( NotifyMessage );
}
